package com.example.distributelock.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * @describte:
 * @program:distribute-lock
 * @author:zhengdp
 * @create:2021-09-22 15:27:25
 **/
@RestController
@Slf4j
public class RedisLockController {

    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping("redisLock")
    public String redisLock(){
        log.info("我进入了方法");
        String key = "redisKey";
        String value = UUID.randomUUID().toString();
        // redisCallback 就是要执行的Redis命令
        RedisCallback<Boolean> redisCallback = redisConnection -> {
            // 如果key不存在，设置成功，如果存在，设置不成功 (设置NX）
            RedisStringCommands.SetOption setOption= RedisStringCommands.SetOption.ifAbsent();
            // 设置过期时间
            Expiration expiration = Expiration.seconds(30);
            // 序列化Key
            byte[] redisKey = redisTemplate.getKeySerializer().serialize(key);
            // 序列化Value
            byte[] redisValue = redisTemplate.getValueSerializer().serialize(value);
            // 执行 setNx操作
            Boolean set = redisConnection.set(redisKey, redisValue, expiration, setOption);
            return set;
        };

        // 获取分布式锁
        Boolean lock = (Boolean) redisTemplate.execute(redisCallback);
        if(lock){
            log.info("我进入了锁！！");
            try{
                Thread.sleep(15000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                // 使用Lua脚本释放锁
                String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1] then\n" +
                        "  return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "  return 0\n" +
                        "end";
                RedisScript<Boolean> redisScript = RedisScript.of(script,Boolean.class);
                List keys = Arrays.asList(key);
                Boolean result = (Boolean) redisTemplate.execute(redisScript,keys,value);
                log.info("释放锁的结果"+result);
            }
        }
        log.info("方法执行完成");
        return "方法执行完成";
    }
}
